package com.ilikesoup.instant.enums.basic;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 扩展了 SelectEnum
 * 使用 Map 作为返回值，以支持更灵活的扩展，摆脱 {@link SelectEnum.SelectVo} 的限制
 *
 * 支持自定义Map编码和描述的键名称。
 *
 * 提供了静态方法返回 Map 列表，与 SelectEnum 中的静态方法类似。
 * {@link MapEnum#list}
 * {@link MapEnum#asList}
 * 两个方法均有重载，在没有重写 {@link MapEnum#getCodeKey} 和 {@link MapEnum#getDescKey} 的情况下，
 * 直接调用重载的静态方法，通过参数指定键名
 *
 * Map 默认使用 {@link HashMap} 实现
 * 可以重写 {@link MapEnum#getInitMap} 来替换实现类
 *
 * JDK-8141508:
 *  <T extends Enum<T> & MapEnum<K>, K>
 *  创建流时先将其标记为需要使用的类型 Arrays.<MapEnum<K>>stream(...
 *
 * @author  Soup
 * @param <K>   枚举项code的类型
 */
public interface MapEnum<K> extends SelectEnum<K> {

    String CODE_KEY = "code";
    String DESC_KEY = "desc";

    /**
     * 使用Map作为返回值
     */
    default Map<String, Object> getMapVo() {
        return getMapVo(getCodeKey(), getDescKey());
    }

    /**
     * 支持自定义的属性名称
     * @param codeKey   自定义编码属性名称
     * @param descKey   自定义描述属性名称
     */
    default Map<String, Object> getMapVo(String codeKey, String descKey) {
        Map<String, Object> vo = getInitMap();
        vo.put(codeKey, getCode());
        vo.put(descKey, getDesc());
        return vo;
    }

    /**
     * 用于指定返回的 Map实例，也可以向其中初始化一些键值对
     */
    default Map<String, Object> getInitMap() {
        return new HashMap<>(4);
    }

    /**
     * 获取编码的key
     */
    @SuppressWarnings("SameReturnValue")
    default String getCodeKey() {
        return CODE_KEY;
    }

    /**
     * 获取描述的key
     */
    @SuppressWarnings("SameReturnValue")
    default String getDescKey() {
        return DESC_KEY;
    }

    /**
     * 将枚举项转换为下拉列表
     *
     * JDK-8142476 - Call site initialization exception caused by LambdaConversionException: Invalid receiver type
     * 基于JDK8存在的 bug，这里必须标识类型
     * Stream.<MapEnum<K>>of
     *
     * @param mapEnumClass   枚举类
     * @param <T>   枚举类型
     * @return  枚举项生成的下拉项列表
     */
    static <T extends Enum<T> & MapEnum<K>, K> List<Map<String, Object>> list(Class<T> mapEnumClass) {
        return Arrays.<MapEnum<K>>stream(mapEnumClass.getEnumConstants())
                .map(MapEnum::getMapVo).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 将枚举项转换为下拉列表
     * 自定义Map的编码和描述的键名称
     */
    static <T extends Enum<T> & MapEnum<K>, K> List<Map<String, Object>> list(String codeKey, String descKey, Class<T> mapEnumClass) {
        return Arrays.<MapEnum<K>>stream(mapEnumClass.getEnumConstants())
                .map(o -> o.getMapVo(codeKey, descKey)).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 将给定的枚举项转换为下拉列表
     *
     * JDK-8142476 - Call site initialization exception caused by LambdaConversionException: Invalid receiver type
     * 基于JDK8存在的 bug，这里必须标识类型
     * Stream.<MapEnum<K>>of
     *
     * @param mapEnums   枚举项数组
     * @param <T>   枚举类型
     * @return  枚举项生成的下拉项列表
     */
    @SafeVarargs
    static <T extends MapEnum<K>, K> List<Map<String, Object>> asList(T... mapEnums) {
        return Arrays.<MapEnum<K>>stream(Objects.requireNonNull(mapEnums))
                .map(MapEnum::getMapVo).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 将给定的枚举项转换为下拉列表
     * 自定义Map的编码和描述的键名称
     */
    @SafeVarargs
    static <T extends MapEnum<K>, K> List<Map<String, Object>> asList(String codeKey, String descKey, T... mapEnums) {
        return Arrays.<MapEnum<K>>stream(Objects.requireNonNull(mapEnums))
                .map(o -> o.getMapVo(codeKey, descKey)).filter(Objects::nonNull).collect(Collectors.toList());
    }

}
